#pragma once

#include "containers.h"
#include "dirs.hpp"
#include "types.h"
#include <cstdio>
#include <cstring>

//
//   ___                               ___
//  (o o)                             (o o)
// (  V  ) May The Force Be With You (  V  )
// --m-m-------------------------------m-m--
//

namespace Trade {

struct ForceLists
{
  DirList df_near_78_st;
  DirList df_far_78_st;
  DirList df_near_rt_st;
  DirList df_far_rt_st;
  DirList df_near_78_cg;
  DirList df_far_78_cg;
  DirList df_near_rt_cg;
  DirList df_far_rt_cg;

  void print(const time_t ts, const price_t hpx) const noexcept
  {
    printf("Near CmP: ");
    df_near_78_st.print_with_hp("near_78_st", ts, hpx);
    df_near_rt_st.print_with_hp("         near_rt_st", ts, hpx);

    printf("Near ChG: ");
    df_near_78_cg.print_with_hp("near_78_cg", ts, hpx);
    df_near_rt_cg.print_with_hp("         near_rt_cg", ts, hpx);

    printf("Far CmP: ");
    df_far_78_st.print_with_hp("far_78_st", ts, hpx);
    df_far_rt_st.print_with_hp("          far_rt_st", ts, hpx);

    printf("Far ChG: ");
    df_far_78_cg.print_with_hp("far_78_cg", ts, hpx);
    df_far_rt_cg.print_with_hp("          far_rt_cg", ts, hpx);
  }
};

struct ForceLists3
{
  DirList3 df_near_78_st;
  DirList3 df_far_78_st;
  DirList3 df_near_rt_st;
  DirList3 df_far_rt_st;
  DirList3 df_near_78_cg;
  DirList3 df_far_78_cg;
  DirList3 df_near_rt_cg;
  DirList3 df_far_rt_cg;

  void print(const time_t ts, const price_t hpx) const noexcept
  {
    printf("Near CmP: ");
    df_near_78_st.print_with_hp("near_78_st", ts, hpx);
    df_near_rt_st.print_with_hp("         near_rt_st", ts, hpx);

    printf("Near ChG: ");
    df_near_78_cg.print_with_hp("near_78_cg", ts, hpx);
    df_near_rt_cg.print_with_hp("         near_rt_cg", ts, hpx);

    printf("Far CmP: ");
    df_far_78_st.print_with_hp("far_78_st", ts, hpx);
    df_far_rt_st.print_with_hp("          far_rt_st", ts, hpx);

    printf("Far ChG: ");
    df_far_78_cg.print_with_hp("far_78_cg", ts, hpx);
    df_far_rt_cg.print_with_hp("          far_rt_cg", ts, hpx);
  }
};

extern void
convert_fl_to_fl3(ForceLists& fl, ForceLists3& fl3);

enum FORCE_EVENT : event_t
{
  FE_NONE = 0x0,
  FE_FD_NEAR_ST_CHANGED = 0x01,
  FE_FD_NEAR_RT_CHANGED = 0x02,
  FE_FD_FAR_ST_CHANGED = 0x04,
  FE_FD_FAR_RT_CHANGED = 0x08,
  FE_CG_NEAR_ST_CHANGED = 0x10,
  FE_CG_NEAR_RT_CHANGED = 0x20,
  FE_CG_FAR_ST_CHANGED = 0x40,
  FE_CG_FAR_RT_CHANGED = 0x80,
};

struct ForceDir
{
  dir_t cg_near_st = 0;
  dir_t cg_near_rt = 0;
  dir_t cg_far_st = 0;
  dir_t cg_far_rt = 0;
  DIR_DREAMER_TYPE changed(const ForceDir& fd,
                           ForceLists& fl,
                           const time_t ts,
                           const double hpx) noexcept;
  void copy(const ForceDir& fd) noexcept;
};

struct ForceLevel
{
  dir_t d_near_10 = 0;
  dir_t d_far_10 = 0;
  dir_t d_near_rt = 0;
  dir_t d_far_rt = 0;
  int l_near_rt = 0;
  int l_far_rt = 0;
  int l_near_10 = 0;
  int l_far_10 = 0;

  bool all() const noexcept
  {
    return (d_near_10 && d_far_10 && d_near_rt && d_far_rt);
  }

  const ForceLevel& operator=(const ForceLevel& fl) noexcept
  {
    memcpy((void*)(this), &fl, sizeof(ForceLevel));
    return fl;
  }

  dir_t get_near_dir() const noexcept {
    return (d_near_10 == d_near_rt && d_near_10) ? d_near_10 : 0;
  }

  dir_t get_far_dir() const noexcept {
    return (d_far_10 == d_far_rt && d_far_10) ? d_far_10 : 0;
  }
};

enum class DIR_TYPE : uint8_t
{
  UNKNOW = 0,
  UP = 1,
  DN = 2,
  SWING = 3,
};

struct ForceSpot
{
  dir_t d = 0;
  level_t l = 0;
  level_t l_max = 0;
  time_t t = 0;
  time_t t_max = 0;
  price_t p = 0;
  price_t p_max = 0;

  void print_diff(const ForceSpot* fs) const noexcept
  {
    printf("(D%d %d | TD: %d | L%d ~ %d : L%d ~ %d | H %.02lff) ",
           d,
           fs->d,
           int(t - fs->t) / 60,
           l,
           l_max,
           fs->l,
           fs->l_max,
           p - fs->p);
  }
  void print(const price_t hpx, const time_t ts) const noexcept
  {
    printf("(D%d %dM L%d ~ %d | %.02lf %.02lf | MAX %dM %.02lf)\n",
           d,
           int (ts - t) / 60,
           l,
           l_max,
           p,
           hpx - p,
           int(ts - t_max) / 60,
           hpx - p_max);
  }
};

using ForceSpot20 = DataArray<ForceSpot, 20>;

#define CA_GET_FORCE(ca, it) ((ForceSpot*)(ca_get_data((ca), (it))))

enum class FORCE_ACTION_TYPE : int
{
  DO_NOTHING,
  ADDNEW,
  UPDATE,
};

struct Force
{
  Force();
  ~Force();

  int new_force_data(const ForceLevel& flv_79,
                     const time_t tm,
                     const price_t hpx) noexcept;
  void* get_near_data_ptr() noexcept { return ca_near.data_ptr; }
  void* get_far_data_ptr() noexcept { return ca_far.data_ptr; }
  usize_t get_deep() noexcept { return deep_; }
  void set_force_data() noexcept;
  void read_data() noexcept;
  void print(const price_t hpx, const time_t t_now) const noexcept;
  ForceSpot* get_data_ptr() noexcept { return fs_data; }
  void clone_data(const Force& force) noexcept;

private:
  static constexpr usize_t deep_ = 20;
public:
  CircleArray ca_near;
  CircleArray ca_far;
private:
  ForceSpot fs_data[deep_ * 2];

  ca_iter near_front() noexcept;
  ca_iter near_back() noexcept;
  ca_iter near_current() noexcept;
  ca_iter near_next(ca_iter it) noexcept;
  ca_iter near_prev(ca_iter it) noexcept;
  ForceSpot* get_near_force_spot(const ca_iter it) noexcept;

  ca_iter far_front() noexcept;
  ca_iter far_back() noexcept;
  ca_iter far_current() noexcept;
  ca_iter far_next(ca_iter it) noexcept;
  ca_iter far_prev(ca_iter it) noexcept;
  ForceSpot* get_far_force_spot(const ca_iter it) noexcept;
};

extern event_t
force_79_changed(const ForceLevel& fl_new, const ForceLevel& fl_old) noexcept;

extern void
print_price_force(const PriceDelta& pd,
                  const NearFarTrend& nft,
                  const ForceLevel& flv_79,
                  const time_t t_now) noexcept;

} // namespace
